﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections;
using System.Windows;
using Xceed.Silverlight.Utils;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace Xceed.Silverlight.Controls
{
  public class RangeDataNavigationConfiguration : DataNavigationConfiguration
  {
    #region Start Property

    public static readonly DependencyProperty StartProperty = DependencyProperty.Register(
      "Start",
      typeof( object ),
      typeof( RangeDataNavigationConfiguration ),
      new PropertyMetadata( null, 
        new PropertyChangedCallback( RangeDataNavigationConfiguration.OnRangeRelatedPropertyChanged ) ) );

    public object Start
    {
      get
      {
        return this.GetValue( RangeDataNavigationConfiguration.StartProperty );
      }
      set
      {
        this.SetValue( RangeDataNavigationConfiguration.StartProperty, value );
      }
    }

    #endregion

    #region End Property

    public static readonly DependencyProperty EndProperty = DependencyProperty.Register(
      "End",
      typeof( object ),
      typeof( RangeDataNavigationConfiguration ),
      new PropertyMetadata(
        null,
        new PropertyChangedCallback( RangeDataNavigationConfiguration.OnRangeRelatedPropertyChanged ) ) );

    public object End
    {
      get
      {
        return this.GetValue( RangeDataNavigationConfiguration.EndProperty );
      }
      set
      {
        this.SetValue( RangeDataNavigationConfiguration.EndProperty, value );
      }
    }

    #endregion

    #region Step Property

    public static readonly DependencyProperty StepProperty = DependencyProperty.Register(
      "Step",
      typeof( object ),
      typeof( RangeDataNavigationConfiguration ),
      new PropertyMetadata( null,
        new PropertyChangedCallback( RangeDataNavigationConfiguration.OnRangeRelatedPropertyChanged ) ) );

    public object Step
    {
      get
      {
        return this.GetValue( RangeDataNavigationConfiguration.StepProperty );
      }
      set
      {
        this.SetValue( RangeDataNavigationConfiguration.StepProperty, value );
      }
    }

    #endregion

    public override IList GetKeys()
    {
      return new ReadOnlyObservableCollection<object>( m_keys );
    }

    protected virtual void UpdateKeys()
    {
#if !WINDOWS_PHONE
      if( !this.IsInitialized )
        return;
#endif

      double calculatedStart;
      double calculatedEnd;
      double calculatedStep;
      Type keyDataType = this.ExtractDataTypeAndValues( out calculatedStart, out calculatedEnd, out calculatedStep );

      using( m_keys.DeferPropertyAndCollectionChanged() )
      {
        m_keys.Clear();

        if( calculatedStart < calculatedEnd )
        {
          for( double i = calculatedStart; i <= calculatedEnd; i += calculatedStep )
          {
            m_keys.Add( System.Convert.ChangeType( i, keyDataType, System.Globalization.NumberFormatInfo.InvariantInfo ) );
          }
        }
        else if( calculatedEnd < calculatedStart )
        {
          for( double i = calculatedStart; i >= calculatedEnd; i -= calculatedStep )
          {
            m_keys.Add( System.Convert.ChangeType( i, keyDataType, System.Globalization.NumberFormatInfo.InvariantInfo ) );
          }
        }
        else
        {
          m_keys.Add( System.Convert.ChangeType( calculatedStart, keyDataType, System.Globalization.NumberFormatInfo.InvariantInfo ) );
        }
      }
    }

    private Type ExtractDataTypeAndValues( out double calculatedStart, out double calculatedEnd, out double calculatedStep )
    {
      object start = this.Start;
      object end = this.End;
      object step = this.Step;

      if( ( start == null ) && ( end == null ) )
      {
        throw new InvalidOperationException( "The start and end values of the RangeDataNavigationConfiguration must be specified." );
      }

      Type keyDataType = ( start != null ) ? start.GetType() : end.GetType();

      if( start == null )
      {
        start = System.Convert.ChangeType( 0.0d, keyDataType, System.Globalization.NumberFormatInfo.InvariantInfo );
      }
      else if( start is string )
      {
        throw new InvalidOperationException( "The start value of the RangeDataNavigationConfiguration cannot be a string. If set in XAML, the data type must be explicitly defined." );
      }
      else if( !( start is ValueType ) )
      {
        throw new InvalidOperationException( "The start value of the RangeDataNavigationConfiguration must be a value type." );
      }

      if( end == null )
      {
        end = System.Convert.ChangeType( 0.0d, keyDataType, System.Globalization.NumberFormatInfo.InvariantInfo );
      }
      else if( end is string )
      {
        throw new InvalidOperationException( "The end value of the RangeDataNavigationConfiguration cannot be a string. If set in XAML, the data type must be explicitly defined." );
      }
      else if( !( end is ValueType ) )
      {
        throw new InvalidOperationException( "The end value of the RangeDataNavigationConfiguration must be a value type." );
      }

      calculatedStart = System.Convert.ToDouble( start );
      calculatedEnd = System.Convert.ToDouble( end );

      if( step == null )
      {
        if( calculatedStart != calculatedEnd )
        {
          step = System.Convert.ChangeType( 1.0d, keyDataType, System.Globalization.NumberFormatInfo.InvariantInfo );
        }
        else
        {
          step = System.Convert.ChangeType( 0.0d, keyDataType, System.Globalization.NumberFormatInfo.InvariantInfo );
        }
      }
      else if( step is string )
      {
        throw new InvalidOperationException( "The step value of the RangeDataNavigationConfiguration cannot be a string. If set in XAML, the data type must be explicitly defined." );
      }
      else if( !( step is ValueType ) )
      {
        throw new InvalidOperationException( "The step value of the RangeDataNavigationConfiguration must be a value type." );
      }


      if( ( start.GetType() != end.GetType() ) || ( start.GetType() != step.GetType() ) )
      {
        throw new InvalidOperationException( "The start, end, and step values of the RangeDataNavigationConfiguration must be of the same data type." );
      }

      calculatedStep = Math.Abs( System.Convert.ToDouble( step ) );

      return keyDataType;
    }

    protected override void EndInitCore()
    {
      base.EndInitCore();
      this.UpdateKeys();
    }

    private static void OnRangeRelatedPropertyChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      RangeDataNavigationConfiguration configuration = sender as RangeDataNavigationConfiguration;

      if( configuration == null )
        return;

      configuration.UpdateKeys();
    }

    private DeferableObservableCollection<object> m_keys = new DeferableObservableCollection<object>();
  }
}
